#include "Avalon/WebServices/Uri.hpp"
#include <boost/lexical_cast.hpp>
#include <boost/regex.hpp>

using namespace Avalon;
using namespace Avalon::WebServices;
using namespace boost;
using namespace std;

namespace {
  regex uriPattern("^((\\w+):)?(\\/\\/(((\\w*):(\\w*)@)?"
                   "([^\\/\\?:]*)(:(\\d+))?))?(\\/?([^\\/\\?#][^\\?#]*)"
                   "?)?(\\?([^#]+))?(#(\\w*))?");
  const int SCHEME_CAPTURE = 2;
  const int USERNAME_CAPTURE = 6;
  const int PASSWORD_CAPTURE = 7;
  const int HOSTNAME_CAPTURE = 8;
  const int PORT_CAPTURE = 10;
  const int PATHNAME_CAPTURE = 11;
  const int QUERY_CAPTURE = 14;
  const int FRAGMENT_CAPTURE = 16;
}

Uri::Uri()
    : m_port(0) {}

Uri::Uri(const string& source) {
  smatch matcher;
  if(!regex_match(source, matcher, uriPattern)) {
    BOOST_THROW_EXCEPTION(MalformedUriException("URI is not well formed."));
  }
  if(matcher[PORT_CAPTURE].str().empty()) {
    m_port = 0;
  } else {
    m_port = lexical_cast<unsigned short>(matcher[PORT_CAPTURE].str());
  }
  m_scheme = matcher[SCHEME_CAPTURE];
  m_username = matcher[USERNAME_CAPTURE];
  m_password = matcher[PASSWORD_CAPTURE];
  m_hostname = matcher[HOSTNAME_CAPTURE];
  m_path = matcher[PATHNAME_CAPTURE];
  m_query = matcher[QUERY_CAPTURE];
  m_fragment = matcher[FRAGMENT_CAPTURE];;
}

const string& Uri::GetScheme() const {
  return m_scheme;
}

const string& Uri::GetUsername() const {
  return m_username;
}

const string& Uri::GetPassword() const {
  return m_password;
}

const string& Uri::GetHostname() const {
  return m_hostname;
}

unsigned short Uri::GetPort() const {
  return m_port;
}

const string& Uri::GetPath() const {
  return m_path;
}

const string& Uri::GetQuery() const {
  return m_query;
}

const string& Uri::GetFragment() const {
  return m_fragment;
}

string Uri::ToString() const {
  string representation;
  if(!m_scheme.empty()) {
    representation = m_scheme + ":";
  }
  if(!m_hostname.empty()) {
    representation += "//";
  }
  if(!m_username.empty()) {
    representation += m_username + ":" + m_password + "@";
  } else if(!m_password.empty()) {
    representation += ":" + m_password + "@";
  }
  representation += m_hostname;
  if(m_port != 0) {
    representation += ":" + lexical_cast<string>(m_port);
  }
  representation += m_path;
  if(!m_query.empty()) {
    representation += "?" + m_query;
  }
  if(!m_fragment.empty()) {
    representation += "#" + m_fragment;
  }
  return representation;
}

Uri::Uri(const string& scheme, const string& username,
    const string& password, const string& hostname, unsigned short port,
    const string& path, const string& query, const string& fragment)
    : m_scheme(scheme),
      m_username(username),
      m_password(password),
      m_hostname(hostname),
      m_port(port),
      m_path(path),
      m_query(query),
      m_fragment(fragment) {}

MalformedUriException::MalformedUriException()
    : runtime_error("Malformed URI.") {}

MalformedUriException::MalformedUriException(const string& message)
    : runtime_error(message) {}

MalformedUriException::~MalformedUriException() throw() {}
